package pub.cleangao.common.util;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


/**
 * 扫描包下路径
 * 包括本地文件和jar包文件
 *
 * @author ljb
 */
public class Scanner {


    public static Set<Class<?>> lookupClassFile(String packageName) {
        String extension = ".class";
        Set<String> set = lookupFile(packageName, extension);
        if (set.size() == 0) {
            return Collections.EMPTY_SET;
        }
        Set<Class<?>> rst = new HashSet<>(set.size());
        try {
            for (String className : set) {
                Class<?> clazz = Resources.classForName(className.replace(extension, ""));
                rst.add(clazz);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rst;
    }

    public static Map<String, InputStream> lookupPlainFile(String packageName, String extension) {
        Set<String> set = lookupFile(packageName, extension);
        Map<String, InputStream> rst = new HashMap<>();
        if (set.size() == 0) {
            return rst;
        }
        try {
            for (String filePath : set) {
                InputStream is = Resources.getResourceAsStream(filePath);
                rst.put(filePath, is);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rst;
    }

    /**
     * 加载包下面全部的.class文件
     */
    public static Set<String> lookupFile(String packageName, String extension) {
        Set<String> rst = new HashSet<>();
        URL url = null;
        try {
            url = Resources.getResourceURL(packageName.replace(".", "/"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String protocol = url.getProtocol();
        if ("file".equals(protocol)) {
            findInLocal(packageName, extension, rst);
        } else if ("jar".equals(protocol)) {
            findInJar(packageName, extension, rst);
        }
        return rst;
    }

    /**
     * 本地查找
     *
     * @param packageName 查找包名
     * @param extension   扩展名 （.class）
     */
    private static void findInLocal(final String packageName, String extension, Set<String> rst) {
        URI url = null;
        try {
            url = Resources.getResourceURL(packageName.replace(".", "/")).toURI();
        } catch (Exception e1) {
            throw new RuntimeException("resource not found.");
        }
        File file = new File(url);
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        for (File childFile : files) {
            if (childFile.getName().endsWith(extension)) {
                rst.add(packageName + "." + childFile.getName());
            }
            if (childFile.isDirectory()) {
                findInLocal(packageName + "." + childFile.getName(), extension, rst);
            }
        }

    }

    /**
     * jar包查找
     *
     * @param packageName 待搜索的包
     * @param extension   扩展名
     */
    private static void findInJar(final String packageName, String extension, Set<String> rst) {
        String pathName = packageName.replace(".", "/");
        JarFile jarFile = null;
        try {
            URL url = Resources.getResourceURL(pathName);
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            jarFile = jarURLConnection.getJarFile();
        } catch (IOException e) {
            throw new RuntimeException("resource not found.");
        }
        Enumeration<JarEntry> jarEntries = jarFile.entries();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String jarEntryName = jarEntry.getName();
            if (jarEntryName.contains(pathName) && !jarEntryName.equals(pathName + "/")) {
                if (jarEntry.getName().endsWith(extension)) {
                    rst.add(jarEntry.getName().replace("/", "."));
                }
                if (jarEntry.isDirectory()) {
                    String clazzName = jarEntryName.replace("/", ".");
                    int endIndex = clazzName.lastIndexOf(".");
                    if (endIndex > 0) {
                        String prefix = clazzName.substring(0, endIndex);
                        findInJar(prefix, extension, rst);
                    }
                }
            }
        }
    }


}
